﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace NFixedPoint
{
    [Serializable]
    public struct FixedPoint3
    {
        public FixedPoint x;
        public FixedPoint y;
        public FixedPoint z;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public FixedPoint3(FixedPoint x, FixedPoint y, FixedPoint z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        #region 常量

        public static FixedPoint3 zero => new FixedPoint3(0, 0, 0);
        public static FixedPoint3 one => new FixedPoint3(1, 1, 1);
        public static FixedPoint3 half => one / 2;
        public static FixedPoint3 forward => new FixedPoint3(0, 0, 1);
        public static FixedPoint3 back => new FixedPoint3(0, 0, -1);
        public static FixedPoint3 left => new FixedPoint3(-1, 0, 0);
        public static FixedPoint3 right => new FixedPoint3(1, 0, 0);
        public static FixedPoint3 up => new FixedPoint3(0, 1, 0);
        public static FixedPoint3 down => new FixedPoint3(0, -1, 0);

        #endregion

        #region 运算符重载


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator +(FixedPoint3 v1, FixedPoint3 v2) =>
            new FixedPoint3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator -(FixedPoint3 v1, FixedPoint3 v2) =>
            new FixedPoint3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator *(FixedPoint3 v1, FixedPoint3 v2) =>
            new FixedPoint3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator *(FixedPoint3 v1, float v2) =>
            new FixedPoint3(v1.x * v2, v1.y * v2, v1.z * v2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator *(FixedPoint3 v1, FixedPoint v2) =>
            new FixedPoint3(v1.x * v2, v1.y * v2, v1.z * v2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator *(FixedPoint v2, FixedPoint3 v1) =>
            new FixedPoint3(v1.x * v2, v1.y * v2, v1.z * v2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator /(FixedPoint3 v1, FixedPoint v2) =>
            new FixedPoint3(v1.x / v2, v1.y / v2, v1.z / v2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 operator -(FixedPoint3 v1) =>
            new FixedPoint3(-v1.x, -v1.y, -v1.z);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 Multiply(FixedPoint3 v1, FixedPoint3 v2) =>
            new FixedPoint3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator ==(FixedPoint3 v1, FixedPoint3 v2)
        {
            return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(FixedPoint3 v1, FixedPoint3 v2)
        {
            return v1.x != v2.x || v1.y != v2.y || v1.z != v2.z;
        }

        #endregion

        #region 向量相关

        //长度的平方
        public FixedPoint unsafeSqrMagnitude => x * x + y * y + z * z;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint UnsafeSqrMagnitude(FixedPoint3 v)
        {
            return v.unsafeSqrMagnitude;
        }

        //模
        public FixedPoint magnitude => FMath.Sqrt(this.unsafeSqrMagnitude);

        //单位向量= 乘以模的倒数
        public FixedPoint3 normalized
        {
            get
            {
                var mag = FMath.Sqrt(this.unsafeSqrMagnitude);
                if (magnitude == 0)
                    return FixedPoint3.zero; //避免0 的情况
                FixedPoint r = FixedPoint.one / mag;
                return new FixedPoint3(x * r, y * r, z * r);
            }
        }

        //点乘
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint Dot(FixedPoint3 v1, FixedPoint3 v2)
        {
            return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
        } //点乘

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static long DotLong(FixedPoint3 v1, FixedPoint3 v2)
        {
            return (long)v1.x * (long)v2.x + (long)v1.y * (long)v2.y + (long)v1.z * (long)v2.z;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 Cross(FixedPoint3 v1, FixedPoint3 v2)
        {
            FixedPoint3 r;
            r.x = v1.y * v2.z - v1.z * v2.y;
            r.y = v1.z * v2.x - v1.x * v2.z;
            r.z = v1.x * v2.y - v1.y * v2.x;
            return r;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint3 Cross(ref FixedPoint3 v1, ref FixedPoint3 v2)
        {
            FixedPoint3 r;
            r.x = v1.y * v2.z - v1.z * v2.y;
            r.y = v1.z * v2.x - v1.x * v2.z;
            r.z = v1.x * v2.y - v1.y * v2.x;
            return r;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static FixedPoint Distance(FixedPoint3 v1, FixedPoint3 v2)
        {
            FixedPoint3 v;
            v.x.raw = v1.x.raw - v2.x.raw;
            v.y.raw = v1.y.raw - v2.y.raw;
            v.z.raw = v1.z.raw - v2.z.raw;
            return UnsafeSqrMagnitude(v);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Factor AngleFactor(FixedPoint3 v1, FixedPoint3 v2)
        {
            var dot = Dot(v1, v2);
            var mo = v1.magnitude * v2.magnitude;
            var cos = dot / mo;
            return FMath.Acos(cos);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float Angle(FixedPoint3 v1, FixedPoint3 v2)
        {
            var dot = Dot(v1, v2);
            var mo = v1.magnitude * v2.magnitude;
            var cos = dot / mo;
            return (float)FMath.Acos(cos).AsFloat();
        }
        #endregion

        #region 其他函数

        public override bool Equals(object obj)
        {
            var v = (FixedPoint3)obj;
            return x == v.x && y == v.y && z == v.z;
        }

        public override int GetHashCode()
        {
            return (int)(x.raw * 16777619 ^ y.raw * 33554467 ^ z.raw * 67108879);
        }

        public override string ToString()
        {
            return $"[FP3]= x:{x} y:{y} z:{z}";
        }
        #endregion
    }
}
